package com.battery.minapp.redis.lock;

import com.battery.minapp.redis.RedisServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Random;

@Component
public class RedisCommonLock {

    public static final Long DEFAULT_TIME_OUT = 15 * 1000L;

    public static final Integer DEFAULT_EXPIRE = 15;

    // 纳秒和毫秒之间的转换率
    public static final long MILLI_NANO_TIME = 1000 * 1000L;

    public static final String LOCKED = "TRUE";

    public static final Random RANDOM = new Random();


    @Autowired
    private RedisServiceImpl redisService;


    private static RedisCommonLock redisCommonLock;


    @PostConstruct
    public void init() {
        redisCommonLock = this;
    }


    public static boolean lock(long timeout, int expire, String key) {
        long nanoTime = System.nanoTime();
        timeout *= MILLI_NANO_TIME;
        try {
            // 在timeout的时间范围内不断轮询锁
            while (System.nanoTime() - nanoTime < timeout) {
                // 锁不存在的话，设置锁并设置锁过期时间，即加锁
                if (redisCommonLock.redisService.setnx(key, LOCKED) == 1) {
                    redisCommonLock.redisService.expire(key, expire);// 设置锁过期时间是为了在没有释放
                    // 锁的情况下锁过期后消失，不会造成永久阻塞
                    return true;
                }
                System.out.println("出现锁等待");
                // 短暂休眠，避免可能的活锁
                Thread.sleep(3, RANDOM.nextInt(30));
            }
        } catch (Exception e) {
            throw new RuntimeException("locking error", e);
        }
        return false;
    }

    /**
     * 设置一个默认过期时间的锁
     *
     * @param key
     * @return
     */
    public static boolean commonLock(String key) {
        return RedisCommonLock.lock(DEFAULT_TIME_OUT, DEFAULT_EXPIRE, key);
    }

    /**
     * 设置一个自定义过期时间的锁
     *
     * @param key
     * @param expire --秒
     * @return
     */
    public static boolean expireLock(String key, int expire) {
        return RedisCommonLock.lock(DEFAULT_TIME_OUT, expire, key);
    }

    public static void unlock(String key) {
        try {
            redisCommonLock.redisService.del(key);// 直接删除
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public static String getLockKey(String purpose, String key) {
        return purpose + "_" + key + "_lock";
    }
}
